/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 */

import path from 'path';

import { HosPrjSdkConfig, HosPrjSdkType, SimpleHosPrjSdkHandler, UniSdkInfoHandler , HosVersionMapper } from '@ohos/hos-sdkmanager-common';
import { DurationEvent, MetricFactory , HvigorTaskGroupType } from '@ohos/hvigor';
import { Component } from '@ohos/sdkmanager-common';
import fse from 'fs-extra';

import { CommonConst } from '../const/common-const.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { updateMetaCompileSdkVersion } from '../utils/meta-util.js';
import { DefaultProgress } from './lib/default-progress.js';
import { Property } from './lib/property-get.js';
import { proxyFun } from './sdk-util.js';

import ApiMeta = ProjectBuildProfile.ApiMeta;


const _log = OhosLogger.getLogger('SdkLoader');

export class HmosSdkLoader {
  private static container?: HmosSdkLoader;
  private hmsSdkMap: Map<string, Map<string, Component>> = new Map();
  private sdkMap: Map<string, Map<string, Component>> = new Map();

  private hmosSdkInfoHandler?: UniSdkInfoHandler;
  private ohosSdkInfoHandler?: UniSdkInfoHandler;

  private property: Property;

  private constructor() {
    this.property = new Property();
    this.setHosMetaCompileSdkVersion();
  }

  static getInstance() {
    if (!HmosSdkLoader.container) {
      HmosSdkLoader.container = new HmosSdkLoader();
    }
    return HmosSdkLoader.container;
  }

  setHosMetaCompileSdkVersion() {
    const hosVersionMapperIntance = HosVersionMapper.INSTANCE;
    const latestSupportVersion = hosVersionMapperIntance.getLatestSupportVersion();
    const hosVersion = hosVersionMapperIntance.getHosVersionByFullBaseApi(latestSupportVersion.getFullBaseApi().getValue());
    const harmonySdkVersion = `${hosVersion}(${latestSupportVersion.getFullBaseApi().getValue()})`;
    updateMetaCompileSdkVersion(harmonySdkVersion);
  }

  /**
   * 获取HarmonyOS sdk中的openharmony
   *
   * @param sdkVersion
   * @param components
   */
  async getHmosSdkComponents(sdkVersion: ApiMeta, components: string[]) {
    // 检查缓存中是否存在
    this.validateCache();
    const versionComponents: Map<string, Component> = this.sdkMap.get(sdkVersion.fullVersion) ?? new Map();
    if (this.checkComponentExistence(components, versionComponents, false)) {
      const result: Map<string, Component> = new Map();
      components.forEach((component) => result.set(component, versionComponents.get(component)!));
      return result;
    }

    // 经过路径检查失败, 不满足所需组件, 初始化SdkHandler并全量扫描本地
    if (!this.ohosSdkInfoHandler) {
      this.initHandler();
    }

    // 经过预测检查, 本地不存在所需组件, 去扫描或下载组件并添加到缓存中
    const durationEvent: DurationEvent = MetricFactory.createDurationEvent('harmonyOS sdk scan', 'HarmonyOS sdk scan', HvigorTaskGroupType.OTHER_TASK_GROUP);
    durationEvent.log.debug(`Local scan or download HarmonyOS sdk components ${components.join(',')}`);
    const localSdks = this.ohosSdkInfoHandler!.getLocalSdks(`${sdkVersion.fullVersion}`);
    if (!this.checkComponentExistence(components, localSdks, false)) {
      _log.printErrorExit('SDK_COMPONENT_MISSING');
    }
    for (const [key, component] of localSdks.entries()) {
      versionComponents.set(key, component);
    }
    this.sdkMap.set(sdkVersion.fullVersion, versionComponents);
    durationEvent.stop();

    // 经过下载, 默认已存在所有组件, 返回结果
    const result: Map<string, Component> = new Map();
    components.forEach((component) => result.set(component, versionComponents.get(component)!));
    return result;
  }

  private checkComponentExistence(components: string[], versionComponents: Map<string, Component>, isHms: boolean) {
    for (const component of components) {
      const componentObj: Component | undefined = versionComponents.get(component);
      if (!componentObj) {
        return false;
      }
      const definitionFileName = isHms ? CommonConst.HMS_SDK_COMPONENT_DEFINITION : CommonConst.OHOS_SDK_COMPONENT_DEFINITION;
      const definitionFile: string = path.resolve(componentObj.getLocation() ?? '', definitionFileName);
      if (!fse.existsSync(definitionFile)) {
        return false;
      }
    }
    return true;
  }

  /**
   * 获取HarmonyOS sdk中的hmscore
   *
   * @param sdkVersion
   * @param components
   */
  async getHmsSdkComponents(sdkVersion: ApiMeta, components: string[]) {
    // 检查缓存中是否存在, 存在则返回结果
    this.validateCache();
    const versionComponents: Map<string, Component> = this.hmsSdkMap.get(sdkVersion.api) ?? new Map();
    if (this.checkComponentExistence(components, versionComponents, true)) {
      const result: Map<string, Component> = new Map();
      components.forEach((component) => result.set(component, versionComponents.get(component)!));
      return result;
    }

    // 经过预测检查, 本地不存在所需组件, 去扫描或下载组件并添加到缓存中
    if (!this.hmosSdkInfoHandler) {
      this.initHandler();
    }

    // 经过预测检查, 本地不存在所需组件, 去扫描或下载组件并添加到缓存中
    const durationEvent: DurationEvent = MetricFactory.createDurationEvent('hmscore sdk scan', 'Hmscore sdk scan', HvigorTaskGroupType.OTHER_TASK_GROUP);
    durationEvent.log.debug(`Local scan or download hmscore sdk components ${components.join(',')}`);
    const localSdks: Map<string, Component> = this.hmosSdkInfoHandler!.getLocalSdks(sdkVersion.api);
    if (!this.checkComponentExistence(components, localSdks, true)) {
      _log.printErrorExit('SDK_COMPONENT_MISSING');
    }
    for (const [key, component] of localSdks.entries()) {
      versionComponents.set(key, component);
    }
    this.hmsSdkMap.set(sdkVersion.api, versionComponents);
    durationEvent.stop();

    // 经过下载, 默认已存在所有组件, 返回结果
    const result: Map<string, Component> = new Map();
    components.forEach((component) => result.set(component, versionComponents.get(component)!));
    return result;
  }

  initHandler(): void {
    const sdkConfig: HosPrjSdkConfig = HosPrjSdkConfig.builder(this.property.getHosSdkDir()).sdkProxy(proxyFun);
    const sdkHandler: SimpleHosPrjSdkHandler = new SimpleHosPrjSdkHandler(sdkConfig, new DefaultProgress());
    this.hmosSdkInfoHandler = sdkHandler.getSdkHandler(HosPrjSdkType.HARMONYOS);
    this.ohosSdkInfoHandler = sdkHandler.getSdkHandler(HosPrjSdkType.OPENHARMONY);
  }

  private validateCache() {
    const currentProperty: Property = new Property();
    if (currentProperty.getHosSdkDir() === this.property.getHosSdkDir()) {
      return;
    }
    _log.debug('HarmonyOS sdk dir changed.');
    this.property = currentProperty;
    this.sdkMap.clear();
    this.hmsSdkMap.clear();
    this.initHandler();
  }
}
